package org.icop.model;

import java.util.AbstractCollection;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class MultiSet<E> extends AbstractCollection<E> implements Collection<E>, Iterable<E> {

	private Map<E, Integer> content;
	
	private int size;
	
	public MultiSet() {
		this.content = new HashMap<>();
		this.size = 0;
	}
	
	@Override
	public int size() {
		return this.size;
	}

	@Override
	public boolean isEmpty() {
		return this.size == 0;
	}

	@Override
	public boolean contains(Object o) {
		return this.content.containsKey(o);
	}

	@Override
	public Iterator<E> iterator() {
		return new Iterator<E>() {
			
			Iterator<E> typeIter = content.keySet().iterator();
			E current = null;
			int currentCount = 0;

			@Override
			public boolean hasNext() {
				if (current == null) {
					return typeIter.hasNext();
				}
				else {
					if (currentCount < content.get(current))
						return true;
					else 
						return typeIter.hasNext();
				}
			}

			@Override
			public E next() {
				if (current == null) {
					current = typeIter.next();
					currentCount = 1;
					return current;
				}
				else {
					if (currentCount < content.get(current)) {
						currentCount++;
						return current;
					}
					else {
						current = typeIter.next();
						currentCount = 1;
						return current;
					}
				}
			}

			@Override
			public void remove() {
				if (current == null) {
					throw new IllegalStateException();				
				}
				else {
					content.remove(current);
				}
			}
		};
	}

	@Override
	public boolean add(E e) {
		if (this.content.containsKey(e))
			if (this.content.get(e) == Integer.MAX_VALUE)
				return false;

		if (this.content.containsKey(e))
			this.content.put(e, this.content.get(e)+1);
		else
			this.content.put(e, 1);
		
		this.size++;
		return true;
	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean remove(Object o) {
		if (!this.content.containsKey(o))
			return false;

		this.content.put((E)o, this.content.get(o)-1);
		if (this.content.get(o) == 0)
			this.content.remove(o);
		
		this.size--;
		return true;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		for (E e : content.keySet())
			sb.append(e.toString() + "("+content.get(e)+") ");
		return sb.toString();
	}
	
}
